package edu.ucdavis.gwt.gis.client.gmaps;

import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.core.client.JsArray;
import com.google.gwt.dom.client.Style.Unit;
import com.google.gwt.event.dom.client.BlurEvent;
import com.google.gwt.event.dom.client.BlurHandler;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.dom.client.KeyCodes;
import com.google.gwt.event.dom.client.KeyDownEvent;
import com.google.gwt.event.dom.client.KeyDownHandler;
import com.google.gwt.event.dom.client.MouseOutEvent;
import com.google.gwt.event.dom.client.MouseOutHandler;
import com.google.gwt.event.dom.client.MouseOverEvent;
import com.google.gwt.event.dom.client.MouseOverHandler;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.TextBox;
import com.google.gwt.user.client.ui.VerticalPanel;

import edu.ucdavis.cstars.client.Graphic;
import edu.ucdavis.cstars.client.InfoTemplate;
import edu.ucdavis.cstars.client.MapWidget;
import edu.ucdavis.cstars.client.SpatialReference;
import edu.ucdavis.cstars.client.Graphic.Attributes;
import edu.ucdavis.cstars.client.control.Control;
import edu.ucdavis.cstars.client.dojo.Color;
import edu.ucdavis.cstars.client.geometry.Geometry;
import edu.ucdavis.cstars.client.geometry.Point;
import edu.ucdavis.cstars.client.layers.GraphicsLayer;
import edu.ucdavis.cstars.client.symbol.SimpleLineSymbol;
import edu.ucdavis.cstars.client.symbol.SimpleMarkerSymbol;
import edu.ucdavis.gwt.gis.client.DataManager;
import edu.ucdavis.gwt.gis.client.Debugger;
import edu.ucdavis.gwt.gis.client.ajax.RequestManager;
import edu.ucdavis.gwt.gis.client.ajax.RequestManager.Request;
import edu.ucdavis.gwt.gis.client.extras.CloseButton;
import edu.ucdavis.gwt.gis.client.toolbar.GeocodeResultsPanel;

public class GoogleSearchPanel extends Control {

	private GraphicsLayer graphicsLayer;
	private Graphic marker = null;
	
	private VerticalPanel panel = new VerticalPanel();
	
	//private VerticalPanel results = new VerticalPanel();
	private GeocodeResultsPanel results = new GeocodeResultsPanel(this);
	
	private TextBox tb = new TextBox();
	//private final Geocoder geocoder = new Geocoder();
	private JsArray<Placemark> locs = null;
	//private Marker marker = null;
	private Placemark currentPlace = null;
	
	private MapWidget map = null;
	
	private String gMapsRequestUrl = "";
	
	public GoogleSearchPanel(String key) {
		
		gMapsRequestUrl = "http://maps.google.com/maps/geo?key="+key+"&output=json&gl=us&q=";
		
		results.setWidth("100%");
		panel.setVerticalAlignment(HorizontalPanel.ALIGN_MIDDLE);
		
		HorizontalPanel hp = new HorizontalPanel();
		hp.setVerticalAlignment(HorizontalPanel.ALIGN_MIDDLE);
		hp.add(new HTML("<div style='padding-right: 2px; white-space:nowrap; color: #444444'>Find Location&nbsp; </div>"));
		tb.setStyleName("searchBoxInput");
		hp.add(tb);
		
		tb.addBlurHandler(new BlurHandler(){
			public void onBlur(BlurEvent event) {
				if( results.getResultCount() <= 1){
					results.hide();
				}
			}
		});
		
		panel.add(hp);
		
		tb.addKeyDownHandler(new KeyDownHandler(){
			public void onKeyDown(KeyDownEvent event) {
				if( event.getNativeKeyCode() == KeyCodes.KEY_ENTER ){
					String searchTxt = tb.getText().toLowerCase();
					
					if( searchTxt.contentEquals("debug") ) {
						Debugger.INSTANCE.show();
						tb.setText("");
						return;
					}
					
					results.hide();
					results.clear();
					
					if( searchTxt.matches("-?\\d+.?\\d*\\s*,\\s*-?\\d+.?\\d*") ){
						String[] parts = searchTxt.split(",");
						try {
							double lat = Double.parseDouble(parts[0].replaceAll("\\s", ""));
							double lng = Double.parseDouble(parts[1].replaceAll("\\s", ""));
							if( (lat < -117 && lat > -125) && (lng > 30 && lng < 43)) { // switch lat/lng
								double tmp = lat;
								lat = lng;
								lng = tmp;
							}
							getLocations(lat, lng, new GeocoderCallback(){
								public void onFailure(int statusCode) {
									results.add(new HTML("No Reverse Geocoding Results"));
									results.show();
								}
								public void onSuccess(JsArray<Placemark> locations) {
									onGeoSuccess(locations);
								}
							});
						} catch(Exception e) {
							results.add(new HTML("Looks like you are trying to reverse geocode<br />but error in parse of code"));
							results.show();
						}
					} else {
						if( !searchTxt.matches(".*"+DataManager.INSTANCE.getConfig().getGeocoderBias()) ){
							searchTxt += " "+DataManager.INSTANCE.getConfig().getGeocoderBias();
						}
						getLocations(searchTxt, new GeocoderCallback(){
							public void onFailure(int statusCode) {
								results.clear();
								results.add(new HTML("No Geocoding Results"));
								results.show();
							}
							public void onSuccess(JsArray<Placemark> locations) {
								onGeoSuccess(locations);
							}
						});
					}
				}
			}
		});
		
//		SimplePanel scp = new SimplePanel();
//		scp.setStyleName(GadgetResources.INSTANCE.css().GmapsSearchTool());
		panel.getElement().getStyle().setPadding(5, Unit.PX);
//		scp.add(panel);
		
		initWidget(panel);
	}

	private void getLocations(double lat, double lng, GeocoderCallback callback){
		getLocations(lat + ","+lng, callback);
	}
	
	private void getLocations(String location, final GeocoderCallback callback){
		Point p = DataManager.INSTANCE.getMap().getExtent().getCenter();
		p = (Point) Geometry.webMercatorToGeographic(p);
		String url = gMapsRequestUrl+location+"&ll="+p.getY()+","+p.getX();
		RequestManager.INSTANCE.makeRequest(new Request() {
			@Override
			public void onError() {
				callback.onFailure(-1);
			}
			@Override
			public void onSuccess(JavaScriptObject json) {
				GeocodeResult result = (GeocodeResult) json;
				if( result.getStatusCode() == 200 ) callback.onSuccess(result.getPlacemarks());
				else callback.onFailure(result.getStatusCode());
			}
		}, url);
	}
	
	protected void setMarker(Placemark place){
		currentPlace = place;
		
		if( marker != null ) graphicsLayer.remove(marker);
		
		Point p = Point.create((float) place.getLng(), (float) place.getLat(), SpatialReference.create(4326));
		p = (Point) Geometry.geographicToWebMercator(p);
		
		InfoTemplate it = InfoTemplate.create("Location: <b>"+currentPlace.getLng()+","+currentPlace.getLat(),
				"<b>Address: </b>"+currentPlace.getAddress());
		SimpleMarkerSymbol symbol = SimpleMarkerSymbol.create(
				SimpleMarkerSymbol.StyleType.STYLE_DIAMOND, 
				14, 
				SimpleLineSymbol.create(
						SimpleLineSymbol.StyleType.STYLE_SOLID,
						Color.create(0, 0, 0, 0.7),
						1),
				Color.create(255, 0, 0, 0.7)
		);
		marker = Graphic.create(p, symbol, Attributes.create(), it);
		
		graphicsLayer.add(marker);
		tb.setText(place.getAddress().replaceAll(", USA$", ""));
		map.centerAt(p);
	
	}
	
	protected void onGeoSuccess(JsArray<Placemark> locations) {
		if( locations.length() == 1 ){
			setMarker(locations.get(0));
		} else if ( locations.length() > 1 ){
			locs = locations;
			HorizontalPanel hp = new HorizontalPanel();
			hp.setWidth("100%");
			hp.add(new HTML("Did you mean: "));
			CloseButton close = new CloseButton();
			close.addClickHandler(new ClickHandler(){
				public void onClick(ClickEvent event) {
					results.hide();
				}
			});
			hp.add(close);
			hp.setCellHorizontalAlignment(close, HorizontalPanel.ALIGN_RIGHT);
			results.addResult(hp);
			for( int i = 0; i < locations.length(); i++ ){
				Label l = new Label(locations.get(i).getAddress().replaceAll(", USA$", ""));
				l.getElement().setId("locSearch-"+i);
				l.addMouseOverHandler(new MouseOverHandler(){
					public void onMouseOver(MouseOverEvent event) {
						Label l = (Label) event.getSource();
						l.addStyleDependentName("mouseover");				
					}
				});
				l.addMouseOutHandler(new MouseOutHandler(){
					public void onMouseOut(MouseOutEvent event) {
						Label l = (Label) event.getSource();
						l.removeStyleDependentName("mouseover");				
					}
				});
				l.setStyleName("LabelButton");
				l.addClickHandler(new ClickHandler(){
					public void onClick(ClickEvent event) {
						Label l = (Label) event.getSource();
						int id = Integer.parseInt(l.getElement().getId().replaceAll("locSearch-",	""));
						setMarker(locs.get(id));
						results.hide();
					}
				});
				l.getElement().getStyle().setProperty("borderTop", "1px solid #cccccc");
				results.addResult(l);
			}
			results.show();
		}
	}

	@Override
	public void init(MapWidget mapWidget) {
		
		map = mapWidget;
		graphicsLayer = map.getGraphics();
		//map.addLayer(graphicsLayer);
		

	}
	
	public void test() {
		Point p = map.getExtent().getCenter();
		Window.alert(p.getX()+","+p.getY()+" - "+p.getSpatialReference().getWkid());
		InfoTemplate it = InfoTemplate.create("This is a test","This is only a test");
		SimpleMarkerSymbol symbol = SimpleMarkerSymbol.create(
				SimpleMarkerSymbol.StyleType.STYLE_DIAMOND, 
				14, 
				SimpleLineSymbol.create(
						SimpleLineSymbol.StyleType.STYLE_SOLID,
						Color.create(0, 0, 0, 1),
						1),
				Color.create(255, 0, 0, 0.7)
		);
		marker = Graphic.create(p, symbol, Attributes.create(), it);
		
		graphicsLayer.add(marker);
	}
	
}
